package com.itools.core.service.impl;

import com.itools.core.annotation.FileStrategy;
import com.itools.core.base.CommonResult;
import com.itools.core.em.FmsCodeBean;
import com.itools.core.em.FmsConstants;
import com.itools.core.em.StrategyType;
import com.itools.core.exception.AppException;
import com.itools.core.param.FmsUploadTokenParam;
import com.itools.core.result.FmsDetailRecordResult;
import com.itools.core.result.FmsFileRecordPathResult;
import com.itools.core.result.FmsFileUploadResult;
import com.itools.core.result.FmsMultipartFileResult;
import com.itools.core.service.FileHandleService;
import com.itools.core.service.FileManagerService;
import com.itools.core.service.MinioClientService;
import com.itools.core.system.AbstractService;
import com.itools.core.utils.CollectionUtils;
import com.itools.core.utils.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @project: itools-backend
 * @description:
 * @author: XUCHANG
 * @create: 2021-04-01 16:17
 */
@FileStrategy(value = StrategyType.MINIO)
public class MinioFileManagerServiceImpl extends AbstractService implements FileManagerService {
    @Autowired
    private MinioClientService minioClientService;
    @Autowired
    private Environment environment;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${fms.file.store-root-path}")
    String fileStoreRootPath;
    @Autowired
    private FileHandleService fileHandleService;
    /**
     * 线程池：核心线程：10，非核心线程：40，阻塞队列：100
     */
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 50, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(100));

    /**
     * 批量上传文件
     * @param files 文件
     * @param uploadToken 文件的token
     * @param uploadTokenParam 文件的类型，以及限制条件
     * @return
     */
    @Override
    public CommonResult<List<FmsMultipartFileResult>> mutipartUploadFiles(MultipartFile[] files,
                                                                          String uploadToken,
                                                                          FmsUploadTokenParam uploadTokenParam) {
        //验证文件目录，不存在目录创建目录
        try {
            //执行单个文件上传到minio服务器、
            boolean bucketExists = minioClientService.bucketExists(fileStoreRootPath);
            if (!bucketExists){
                minioClientService.makeBucket(fileStoreRootPath);
            }
        }catch (Exception e){
            logger.error("文件上传到MINIO服务器失败",e);
            throw new AppException(FmsCodeBean.FmsCode.FAIL_UPLOAD_FILE_TO_MINIO.getCode());
        }
        List<FmsMultipartFileResult> results = new ArrayList<>();
        List<FutureTask<FmsMultipartFileResult>> taskList = new ArrayList<>();
        //文件写入服务器
        for (MultipartFile file : files) {
            //文件记录写入数据库
            String uniqueId = UUIDUtils.uuid();
            //创建FutureTask的任务
            FutureTask<FmsMultipartFileResult> futureTask = new FutureTask(new FmsFileUploadMinioTask(file,
                    uploadTokenParam,uniqueId,fileStoreRootPath,fileHandleService,minioClientService));
            taskList.add(futureTask);
            try {
                threadPoolExecutor.execute(futureTask);
            }catch (Exception e){
                logger.error("批量上传文件失败");
                throw new AppException(FmsCodeBean.FmsCode.FILE_MULTIPART_UPLOAD.code);
            }
        }
        for(FutureTask<FmsMultipartFileResult> task: taskList){
            if (task == null){
                logger.error("批量上传文件失败");
                throw new AppException(FmsCodeBean.FmsCode.FILE_MULTIPART_UPLOAD.code);
            }
            try {
                FmsMultipartFileResult fmsMultipartFileResult = task.get();
                results.add(fmsMultipartFileResult);
            } catch (Exception e) {
                logger.error("文件获取失败",e);
            }
        }
        //文件上传成功，删除文件访问的token
        stringRedisTemplate.delete(FmsConstants.FILE_TOKEN_NAME_SPACE + uploadToken);
        return CommonResult.success(results);
    }

    /**
     * 单个上传文件
     *
     * @param file  文件
     * @param uploadToken 文件的token
     * @param uploadTokenParam 文件的类型，以及限制条件
     * @param originalFilename 原名称
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public CommonResult<FmsFileUploadResult> singletonUploadFile(MultipartFile file,
                                                                 String uploadToken,
                                                                 FmsUploadTokenParam uploadTokenParam,
                                                                 String originalFilename) {
        //文件记录写入数据库
        String uniqueId = UUIDUtils.uuid();
        fileHandleService.createAndPersistDBRecord(file,uploadTokenParam,uniqueId);
        try {
            //执行单个文件上传到minio服务器
            boolean bucketExists = minioClientService.bucketExists(fileStoreRootPath);
            if (!bucketExists){
                minioClientService.makeBucket(fileStoreRootPath);
            }
            String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."),originalFilename.length());
            minioClientService.putObject(fileStoreRootPath,file,uniqueId+fileSuffix);
        }catch (Exception e){
            logger.error("文件上传到MINIO服务器失败",e);
            throw new AppException(FmsCodeBean.FmsCode.FAIL_UPLOAD_FILE_TO_MINIO.getCode());
        }

        //文件上传成功，删除文件访问的token
        stringRedisTemplate.delete(FmsConstants.FILE_TOKEN_NAME_SPACE + uploadToken);
        FmsFileUploadResult result = new FmsFileUploadResult();
        result.setUniqueId(uniqueId);
        result.setFileName(originalFilename);
        return CommonResult.success(result);
    }

    /**
     * 获取文件访问路径
     *
     * @param uniqueIds      List，文件唯一id
     * @param expiredTime    过期时间
     * @param maxAccessCount 最大访问次数
     * @param type           文件下载 download/展示 show
     * @return
     */
    @Override
    public CommonResult<List<FmsFileRecordPathResult>> getFileUrlByUniqueIds(List<String> uniqueIds, Integer expiredTime, Integer maxAccessCount, String type) {
        List<FmsFileRecordPathResult> results = new ArrayList<>();
        if (CollectionUtils.isEmpty(uniqueIds)){
            return CommonResult.success(results);
        }
        //获取文件唯一id的文件信息
        CommonResult<List<FmsDetailRecordResult>> recordsResult = fileHandleService.queryFileRecordsByUniqueIds(uniqueIds);
        //key:uniqueId val:文件信息
        Map<String, FmsDetailRecordResult> recordResultMap = recordsResult.getData().stream().collect(Collectors.toMap(FmsDetailRecordResult::getUniqueId, Function.identity(),(key1,key2)->key2));
        for (String uniqueId : uniqueIds) {
            if (!recordResultMap.containsKey(uniqueId)){
                continue;
            }
            FmsDetailRecordResult record = recordResultMap.get(uniqueId);
            String originalFilename = record.getOrigFileName();
            String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."),originalFilename.length());
            //获取预览地址
            String objectUrl = minioClientService.presignedGetObject(fileStoreRootPath, uniqueId+fileSuffix,3600*24*7);
            FmsFileRecordPathResult fmsFileRecordPathResult = new FmsFileRecordPathResult();
            fmsFileRecordPathResult.setPath(objectUrl);
            fmsFileRecordPathResult.setUniqueId(uniqueId);
            results.add(fmsFileRecordPathResult);
        }
        return CommonResult.success(results);
    }

    /**
     * 下载文件
     *
     * @param fileAccessToken 文件的访问token
     * @param response
     * @param uniqueId        文件唯一id
     */
    @Override
    public void downloadFile(String fileAccessToken, HttpServletResponse response, String uniqueId) {
        //校验预览或者下载的文件的令牌

        CommonResult<FmsDetailRecordResult> detailRecordResult = fileHandleService.getFileUploadRecord(uniqueId);
        FmsDetailRecordResult fmsDetailRecordResult = detailRecordResult.getData();
        String originalFilename = fmsDetailRecordResult.getOrigFileName();
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."),originalFilename.length());

        //minio处理文件下载逻辑
        minioClientService.downloadFile(fileStoreRootPath,uniqueId+fileSuffix,originalFilename,response);

    }
}
